/**
 * 
 */
package com.example.android_is2.pathDraw;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.example.android_is2.InfoGeoPoints;
import com.example.android_is2.MainActivity;
import com.google.android.maps.GeoPoint;

import android.util.Log;

/**
 * @author KAIN-PC
 * 
 */
public class GoogleParser extends XMLParser implements Parser {
  /** Distance covered. **/
  private int distance;

  // VARIABILE PER CENTRARE LA MAPPA -> DICHIARAZIONE
  public static GeoPoint start_point_for_center = null;
  private boolean isFirst = false;

  // ---------------------------------------------------

  public GoogleParser(String feedUrl) {
    super(feedUrl);
  }

  /**
   * Parses a url pointing to a Google JSON object to a Route object.
   * 
   * @return a Route object based on the JSON object.
   */

  public Route parse() {
    // turn the stream into a string
    final String result = convertStreamToString(this.getInputStream());
    if (result != null) {
      // Create an empty route
      final Route route = new Route();
      // Create an empty segment
      final Segment segment = new Segment();
      try {
        // Tranform the string into a json object
        final JSONObject json = new JSONObject(result);
        // Get the route object
        final JSONObject jsonRoute = json.getJSONArray("routes").getJSONObject(0);
        // Get the leg, only one leg as we don't support waypoints
        // Get the steps for this leg
        // System.out.println("Dimensione: "+jsonRoute.getJSONArray("legs").length());
        for (int k = 0; k < jsonRoute.getJSONArray("legs").length(); k++) {

          JSONObject leg = jsonRoute.getJSONArray("legs").getJSONObject(k);
          final JSONArray steps = leg.getJSONArray("steps");
          // Number of steps for use in for loop
          final int numSteps = steps.length();
          // Set the name of this route using the start & end addresses
          route.setName(leg.getString("start_address") + " to " + leg.getString("end_address"));
          // Get google's copyright notice (tos requirement)
          route.setCopyright(jsonRoute.getString("copyrights"));
          // Get the total length of the route.
          route.setLength(leg.getJSONObject("distance").getInt("value"));
          // Get any warnings provided (tos requirement)
          if (!jsonRoute.getJSONArray("warnings").isNull(k)) {
            route.setWarning(jsonRoute.getJSONArray("warnings").getString(k));
          }
          /*
           * Loop through the steps, creating a segment for each one and decoding any polylines
           * found as we go to add to the route object's map array. Using an explicit for loop
           * because it is faster!
           */
          for (int i = 0; i < numSteps; i++) {
            // Get the individual step
            final JSONObject step = steps.getJSONObject(i);
            // Get the start position for this step and set it on the segment
            final JSONObject start = step.getJSONObject("start_location");
            final GeoPoint position = new GeoPoint((int) (start.getDouble("lat") * 1E6),
                (int) (start.getDouble("lng") * 1E6));

            // VARIABILE PER CENTRARE LA MAPPA
            if (isFirst == false) {
              isFirst = true;
              start_point_for_center = position;
            }
            // ---------------------------------
            segment.setPoint(position);
            // Set the length of this segment in metres
            final int length = step.getJSONObject("distance").getInt("value");
            distance += length;
            segment.setLength(length);
            segment.setDistance(distance / 1000);
            // Strip html from google directions and set as turn instruction
            segment.setInstruction(step.getString("html_instructions").replaceAll("<(.*?)*>", ""));
            // Retrieve & decode this segment's polyline and add it to the route.
            route.addPoints(decodePolyLine(step.getJSONObject("polyline").getString("points")));
            // Push a copy of the segment to the route
            route.addSegment(segment.copy());
          }
        }
      } catch (JSONException e) {
        Log.e(e.getMessage(), "Google JSON Parser - " + feedUrl);
      }
      return route;
    } else {
      return null;
    }
  }

  // GeoCoding SAVE point
  public InfoGeoPoints parseGeoCoding() {
    // turn the stream into a string
    final String result = convertStreamToString(this.getInputStream());
    if (result != null) {
      // Create an empty geopoint
      InfoGeoPoints latlong_con_testo = null;
      try {
        // Tranform the string into a json object
        final JSONObject json = new JSONObject(result);
        // Get the route object
        final JSONObject jsonResults = json.getJSONArray("results").getJSONObject(0);
        Log.i(MainActivity.TAG + getClass(),
            "ADDRESS: " + (jsonResults.getString("formatted_address")));
        JSONObject geometry = jsonResults.getJSONObject("geometry");
        final JSONObject location = geometry.getJSONObject("location");
        latlong_con_testo = new InfoGeoPoints((location.getDouble("lat")),
            (location.getDouble("lng")),(jsonResults.getString("formatted_address")),"");
        Log.i(MainActivity.TAG+getClass(), "GOOGLE_PARSER: LAT: "+(int) (location.getDouble("lat") * 1E6)+"-LONG: "+(int) (location.getDouble("lng") * 1E6));
        // ---------------------------------
      } catch (JSONException e) {
        Log.e(e.getMessage(), "Google JSON Parser - " + feedUrl);
      }
      return latlong_con_testo;
    } else {
      return null;
    }
  }
  
//GeoCoding SAVE point
 public InfoGeoPoints parseTrackCoding(InfoGeoPoints point) {
   // turn the stream into a string
   final String result = convertStreamToString(this.getInputStream());
   if (result != null) {
     // Create an empty geopoint
     InfoGeoPoints latlong_con_testo = null;
     try {
       // Tranform the string into a json object
       final JSONObject json = new JSONObject(result);
       // Get the route object
       final JSONObject jsonResults = json.getJSONArray("results").getJSONObject(0);
       final String location = jsonResults.getString("formatted_address");
       Log.d(MainActivity.TAG+getClass(), "LOCATION: "+location);
       latlong_con_testo = new InfoGeoPoints(point.getLat(),point.getLong(),"",location);
       Log.i(MainActivity.TAG+getClass(), "GOOGLE_PARSER: LAT: "+point.getLat()+"-LONG: "+point.getLong()+" - LOCATION: "+location);
       // ---------------------------------
     } catch (JSONException e) {
       Log.e(e.getMessage(), "Google JSON Parser - " + feedUrl);
     }
     return latlong_con_testo;
   } else {
     return null;
   }
 }

  // ---------------------------------------------------

  /*
   * public Route parse() { // turn the stream into a string final String result =
   * convertStreamToString(this.getInputStream()); // Create an empty route final Route route = new
   * Route(); // Create an empty segment final Segment segment = new Segment(); try { // Tranform
   * the string into a json object final JSONObject json = new JSONObject(result); // Get the route
   * object final JSONObject jsonRoute = json.getJSONArray("routes").getJSONObject(0); // Get the
   * leg, only one leg as we don't support waypoints final JSONObject leg =
   * jsonRoute.getJSONArray("legs").getJSONObject(0); // Get the steps for this leg
   * //System.out.println("Dimensione: "+jsonRoute.getJSONArray("legs").length()); final JSONArray
   * steps = leg.getJSONArray("steps"); // Number of steps for use in for loop final int numSteps =
   * steps.length(); // Set the name of this route using the start & end addresses
   * route.setName(leg.getString("start_address") + " to " + leg.getString("end_address")); // Get
   * google's copyright notice (tos requirement)
   * route.setCopyright(jsonRoute.getString("copyrights")); // Get the total length of the route.
   * route.setLength(leg.getJSONObject("distance").getInt("value")); // Get any warnings provided
   * (tos requirement) if (!jsonRoute.getJSONArray("warnings").isNull(k)) {
   * route.setWarning(jsonRoute.getJSONArray("warnings").getString(k)); }
   * 
   * Loop through the steps, creating a segment for each one and decoding any polylines found as we
   * go to add to the route object's map array. Using an explicit for loop because it is faster!
   * 
   * for (int i = 0; i < numSteps; i++) { // Get the individual step final JSONObject step =
   * steps.getJSONObject(i); // Get the start position for this step and set it on the segment final
   * JSONObject start = step.getJSONObject("start_location"); final GeoPoint position = new
   * GeoPoint((int) (start.getDouble("lat") * 1E6), (int) (start.getDouble("lng") * 1E6));
   * 
   * //VARIABILE PER CENTRARE LA MAPPA if (isFirst == false) { isFirst = true;
   * start_point_for_center = position; } //---------------------------------
   * segment.setPoint(position); // Set the length of this segment in metres final int length =
   * step.getJSONObject("distance").getInt("value"); distance += length; segment.setLength(length);
   * segment.setDistance(distance / 1000); // Strip html from google directions and set as turn
   * instruction segment.setInstruction(step.getString("html_instructions").replaceAll("<(.*?)*>",
   * "")); // Retrieve & decode this segment's polyline and add it to the route.
   * route.addPoints(decodePolyLine(step.getJSONObject("polyline").getString("points"))); // Push a
   * copy of the segment to the route route.addSegment(segment.copy()); } } catch (JSONException e)
   * { Log.e(e.getMessage(), "Google JSON Parser - " + feedUrl); } return route; }
   */

  /**
   * Convert an inputstream to a string.
   * 
   * @param input
   *          inputstream to convert.
   * @return a String of the inputstream.
   */

  private static String convertStreamToString(final InputStream input) {
    if (input != null) {
      final BufferedReader reader = new BufferedReader(new InputStreamReader(input));
      final StringBuilder sBuf = new StringBuilder();

      String line = null;
      try {
        while ((line = reader.readLine()) != null) {
          sBuf.append(line);
        }
      } catch (IOException e) {
        Log.e(e.getMessage(), "Google parser, stream2string");
      } finally {
        try {
          input.close();
        } catch (IOException e) {
          Log.e(e.getMessage(), "Google parser, stream2string");
        }
      }
      return sBuf.toString();
    } else {
      return null;
    }
  }

  /**
   * Decode a polyline string into a list of GeoPoints.
   * 
   * @param poly
   *          polyline encoded string to decode.
   * @return the list of GeoPoints represented by this polystring.
   */

  private List<GeoPoint> decodePolyLine(final String poly) {
    int len = poly.length();
    int index = 0;
    List<GeoPoint> decoded = new ArrayList<GeoPoint>();
    int lat = 0;
    int lng = 0;

    while (index < len) {
      int b;
      int shift = 0;
      int result = 0;
      do {
        b = poly.charAt(index++) - 63;
        result |= (b & 0x1f) << shift;
        shift += 5;
      } while (b >= 0x20);
      int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
      lat += dlat;

      shift = 0;
      result = 0;
      do {
        b = poly.charAt(index++) - 63;
        result |= (b & 0x1f) << shift;
        shift += 5;
      } while (b >= 0x20);
      int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
      lng += dlng;

      decoded.add(new GeoPoint((int) (lat * 1E6 / 1E5), (int) (lng * 1E6 / 1E5)));
    }

    return decoded;
  }
}
